#ifndef __DEBUG__
// For printing purposes
#define __DEBUG__ 0
#endif

#include <iostream>
#include <cstdint>

#include "../VAR.hpp"
#include "../TERM.hpp"
#include "../EXPRESSION.hpp"
#include "../ADD.hpp"
#include "../SUB.hpp"

using namespace std;

void test3() {

  if (__DEBUG__) cout << endl << "Beginning Test 3:" << endl;

  constexpr Variable<1> v1;
  constexpr VariableTerm<1> t1(v1);
  constexpr Variable<2> v2;
  constexpr VariableTerm<2> t2(v2);
  constexpr Variable<3> v3;
  constexpr VariableTerm<3> t3(v3);

  // Some constant expressions
  constexpr ConstantTerm termc1 = 2*(-2)+4;
  constexpr ConstantTerm termc2 = 2*One+2;
  constexpr ConstantTerm termc3 = 4+4-2*One+2;
  constexpr Expression<ConstantTerm> exprc1(termc1);
  constexpr Expression<ConstantTerm> exprc2(termc2);
  constexpr Expression<ConstantTerm> exprc3(termc3);
  static_assert(termc1.constant, "NOT CONSTANT !");
  static_assert(exprc1.expr_.constant, "NOT CONSTANT !");
  static_assert(termc2.constant, "NOT CONSTANT !");
  static_assert(exprc2.expr_.constant, "NOT CONSTANT !");
  static_assert(termc3.constant, "NOT CONSTANT !");
  static_assert(exprc3.expr_.constant, "NOT CONSTANT !");

  static_assert(termc1.cst() == 0, "WRONG CONSTANT VALUE !");
  static_assert(exprc1.expr_.cst() == 0, "WRONG CONSTANT VALUE !");
  static_assert(termc2.cst() == 4, "WRONG CONSTANT VALUE !");
  static_assert(exprc2.expr_.cst() == 4, "WRONG CONSTANT VALUE !");
  static_assert(termc3.cst() == 8, "WRONG CONSTANT VALUE !");
  static_assert(exprc3.expr_.cst() == 8, "WRONG CONSTANT VALUE !");

  if (__DEBUG__) cout << "\tEXPRC1 evals to " << endl <<  exprc1 << endl;
  if (__DEBUG__) cout << "\tEXPRC2 evals to " << endl <<  exprc2 << endl;
  if (__DEBUG__) cout << "\tEXPRC3 evals to " << endl <<  exprc3 << endl;


  // Some variable expressions
  constexpr Expression<VariableTerm<1> > exprv1 = -2*t1;
  constexpr Expression<VariableTerm<1> > exprv2 = -2*t1*5;
  if (__DEBUG__) cout << endl << "\tEXPRV1 evals to " << endl <<  exprv1 << endl;
  if (__DEBUG__) cout <<         "\tEXPRV2 evals to " << endl <<  exprv2 << endl;

  // Some simple binary expressions
  constexpr BinaryExpression<ConstantTerm, ConstantTerm, Add> exprb1(termc1, termc2);
  if (__DEBUG__) cout << endl << "\tEXPRB1 evals to " << endl <<  exprb1 << endl;     
  static_assert(exprb1.l_.cst() == 0, "WRONG CONSTANT VALUE !");

  constexpr BinaryExpression<
    Expression<VariableTerm<1> >, Expression<VariableTerm<1> >, Add> exprb2(exprv1, exprv2);
  if (__DEBUG__) cout << endl << "\tEXPRB2 evals to " << endl <<  exprb2 << endl;     
  static_assert(exprb1.h_.cst() == 4, "WRONG CONSTANT VALUE !");

  auto exprb3(exprv1 + exprv2);
  if (__DEBUG__) cout << endl << "\tEXPRB3 evals to " << endl <<  exprb3 << endl;      
  if (__DEBUG__) cout << endl << "\tEXPRB3 evals to " << endl <<  (exprv1 + exprv2) << endl;      


  // More complex expressions
  auto expraaa = -2*t1+3*t2;
  if (__DEBUG__) cout << endl << "\tEXPRAAA evals to " << endl <<  expraaa << endl;      
  auto exprbbb = expraaa-2*t1+3*t2;
  if (__DEBUG__) cout << endl << "\tEXPRBBB evals to " << endl <<  exprbbb << endl;      
  auto exprccc = expraaa+3;
  if (__DEBUG__) cout << endl << "\tEXPRCCC evals to " << endl <<  exprccc << endl;      

  {
    // Is this one is too complex, cannot combine expressions but just expressions
    // and terms ?
    auto exprccc = expraaa-2*t1+3*t2+expraaa;
    if (__DEBUG__) cout << endl << "\tEXPRCCC evals to " << endl <<  exprccc << endl;      
  }

  auto expr3 = -2*t1+3*t2-(4*(-3))*t3;
  if (__DEBUG__) cout << endl << "\tEXPR3 evals to " << endl <<  expr3 << endl;

  auto expr4 = -2*t1+4+3*t2-(4*(-3))*t3-5;
  if (__DEBUG__) cout << endl << "\tEXPR4 evals to " << endl <<  expr4 << endl;

}

int main (void) {
  test3();
}
